package com.enfi.api.modules.db.base;

import com.enfi.api.util.SpeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.CrudMethodMetadata;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;

import javax.persistence.criteria.Predicate;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * @author wind
 * @since 2020-04-07 15:47:34
 */
public abstract class BaseServiceImpl<R extends JpaRepositoryImplementation<T,ID>,T,ID> implements BaseService<R,T,ID> {

    protected R baseRepository;


    @Override
    public Class<T> getEntityClass() {
        Type genType = getClass().getGenericSuperclass();
        ParameterizedType p=(ParameterizedType)genType;
        Class c=(Class)p.getActualTypeArguments()[1];
        return c;
    }

    @Autowired
    @Override
    public void setRepository(R r) {
        this.baseRepository=r;
    }

    @Override
    public Page find(MatchQuery matchQuery, T t, Pageable pageable){
        BaseQuery baseQuery=new BaseQuery();
        baseQuery.setQuery(matchQuery);
        baseQuery.getQuery().init();
        return (Page) query(baseQuery,t,pageable);
    }

    @Override
    public Page find(BaseQuery baseQuery,T t){
        baseQuery.getQuery().init();
        Pageable pageable= PageRequest.of(0,10);
        return (Page) query(baseQuery,t,pageable);
    }

    @Override
    public boolean update(T t, HttpMethod httpMethod) {
        this.baseRepository.save(t);
        return true;
    }

    @Override
    public boolean update(Collection<T> collection, HttpMethod httpMethod) {
        collection.forEach(v->{
            this.baseRepository.save(v);
        });
        return true;
    }

    @Override
    public boolean insert(T t) {
        this.baseRepository.save(t);
        return true;
    }

    @Override
    public boolean insertAll(Collection<T> collection) {
        this.baseRepository.saveAll(collection);
        return true;
    }

    @Override
    public boolean remove(T t) {
        this.baseRepository.delete(t);
        return true;
    }

    protected Object query(BaseQuery baseQuery,T t,Pageable pageable){
        Assert.isTrue(baseQuery.getQuery()!=null&&baseQuery.getQuery().getQueryType()!=null,"查询方式不为空");
        switch (baseQuery.getQuery().getQueryType()){
            case "spe":
                return this.baseRepository.findAll((root,query,builder)->{
                    List<Predicate> predicates= SpeUtils.getInstance().toSpec(t,baseQuery,root,builder);
                    query.where(predicates.stream().toArray(Predicate[]::new));

                    return null;
                },pageable);
            default:
                Example<T> example=Example.of(t,ExampleMatcher.matching()
                        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING));
                return this.baseRepository.findAll(example,pageable);
        }
    }



    @Override
    public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {

    }

    @Override
    public List<T> findAll() {
        return baseRepository.findAll();
    }

    @Override
    public List<T> findAll(Sort sort) {
        return baseRepository.findAll(sort);
    }

    @Override
    public List<T> findAllById(Iterable<ID> iterable) {
        return baseRepository.findAllById(iterable);
    }

    @Override
    public <S extends T> List<S> saveAll(Iterable<S> iterable) {
        return baseRepository.saveAll(iterable);
    }

    @Override
    public void flush() {
        baseRepository.flush();
    }

    @Override
    public <S extends T> S saveAndFlush(S s) {
        return (S) baseRepository.saveAndFlush(s);
    }

    @Override
    public void deleteInBatch(Iterable<T> iterable) {
        baseRepository.deleteInBatch(iterable);
    }

    @Override
    public void deleteAllInBatch() {
        baseRepository.deleteAllInBatch();
    }

    @Override
    public T getOne(ID id) {
        return (T) baseRepository.getOne(id);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example) {
        return baseRepository.findAll(example);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
        return baseRepository.findAll(example,sort);
    }

    @Override
    public Optional<T> findOne(Specification<T> specification) {
        return baseRepository.findOne(specification);
    }

    @Override
    public List<T> findAll(Specification<T> specification) {
        return baseRepository.findAll(specification);
    }

    @Override
    public Page<T> findAll(Specification<T> specification, Pageable pageable) {
        return baseRepository.findAll(specification,pageable);
    }

    @Override
    public List<T> findAll(Specification<T> specification, Sort sort) {
        return baseRepository.findAll(specification,sort);
    }

    @Override
    public long count(Specification<T> specification) {
        return baseRepository.count(specification);
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        return baseRepository.findAll(pageable);
    }

    @Override
    public <S extends T> S save(S s) {
        return (S) baseRepository.save(s);
    }

    @Override
    public Optional<T> findById(ID id) {
        return baseRepository.findById(id);
    }

    @Override
    public boolean existsById(ID id) {
        return baseRepository.existsById(id);
    }

    @Override
    public long count() {
        return baseRepository.count();
    }

    @Override
    public void deleteById(ID id) {
        baseRepository.deleteById(id);
    }

    @Override
    public void delete(T t) {
        baseRepository.delete(t);
    }

    @Override
    public void deleteAll(Iterable<? extends T> iterable) {
        baseRepository.deleteAll(iterable);
    }

    @Override
    public void deleteAll() {
        baseRepository.deleteAll();
    }

    @Override
    public <S extends T> Optional<S> findOne(Example<S> example) {
        return baseRepository.findOne(example);
    }

    @Override
    public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
        return baseRepository.findAll(example,pageable);
    }

    @Override
    public <S extends T> long count(Example<S> example) {
        return baseRepository.count(example);
    }

    @Override
    public <S extends T> boolean exists(Example<S> example) {
        return baseRepository.exists(example);
    }
}